/*
 * flmparse.c
 *
 *  Created on: 2021年4月10日
 *      Author: hello
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <io.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include "elf.h"

#define debug_printf(fmt, ...)  do{printf(fmt, ##__VA_ARGS__);fflush(stdout);}while(0)

static int ReadDataFromFile(const char* FName, uint32_t offset, void* buf, uint32_t size);
static int FLM_Prase(const void* FName, void* pBuffer, uint32_t* Size, uint32_t* Init, uint32_t* UnInit, uint32_t* EraseChip, uint32_t* EraseSector, uint32_t* ProgramPage);
static int flmgen(const char* filename);

typedef struct
{
	const uint32_t breakpoint;
	const uint32_t static_base;
	const uint32_t stack_pointer;
} program_syscall_t;

typedef struct
{
	const uint32_t init;
	const uint32_t uninit;
	const uint32_t erase_chip;
	const uint32_t erase_sector;
	const uint32_t program_page;
	const program_syscall_t sys_call_s;
	const uint32_t program_buffer;
	const uint32_t algo_start;
	const uint32_t algo_size;
	const uint32_t* algo_blob;
	const uint32_t program_buffer_size;
} program_target_t;

// RAM使用，可设置：2048 4096 8192
//#define MCU_RAM_SIZE 2048
#define MCU_RAM_SIZE 4096
//#define MCU_RAM_SIZE 8192

// 单片机内存基地址
#define MCU_RAM_BASE_ADDR   0x20000000

// 单片机FLASH基地址
//#define MCU_FLASH_BASE_ADDR 0x00000000
#define MCU_FLASH_BASE_ADDR 0x08000000

#if 1
#define myfprintf(stream, template, ...) \
	do { \
		fprintf(stream, template, ##__VA_ARGS__); \
		fflush(stream); \
	}while(0)
#else
#define myfprintf(stream, template, ...)
#endif

#define LOAD_FUN_NUM 5

const char* StrFunNameTable[LOAD_FUN_NUM] = {
	"\nInit\n",
	"\nUnInit\n",
	"\nEraseChip\n",
	"\nEraseSector\n",
	"\nProgramPage\n"
};

uint32_t FlashAlgoBlob[1024*1024] = {0};


/*
FLM算法4K空间分布：
+------------------------------------------------------------------------+
| Algo Blob  | Program Buffer              | Static Data | Stack Pointer |
+------------------------------------------------------------------------+
| < 1K       | 1K             |      1K    | 1K          | 1K            |
+------------------------------------------------------------------------+
| 0x20000000 | 0x20000400     | 0x20000800 | 0x20000C00  | 0x20001000    |
+------------------------------------------------------------------------+

FLM算法2K空间分布：
+-----------------------------------------------------------+
| Algo Blob  | Program Buffer | Static Data | Stack Pointer |
+-----------------------------------------------------------+
| < 1K       | 512 Bytes      | 256 Bytes   | 256 Bytes     |
+-----------------------------------------------------------+
| 0x20000000 | 0x20000400     | 0x20000600  | 0x20000800    |
+-----------------------------------------------------------+
*/

int main(int argc, char* argv[])
{
	if(argc == 1)
	{
	    struct _finddata_t files;
	    int fd;
	    int i = 0;
	    fd = _findfirst("./*.flm", &files);
	    if(fd < 0)
		{
		    debug_printf("ERROR: no flm file found !\n");
			goto __exit;
		}
		do
		{
		    flmgen(files.name);
			i++;
		}while(0 == _findnext(fd, &files));
		_findclose(fd);
	}
	else if (argc == 2)
	{
		flmgen(argv[1]);
	}else{
		debug_printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
		debug_printf("Usage:\n");
		debug_printf("        flmgen.exe [filename]\n");
		debug_printf("\n\n");
		return 0;
	}
__exit:
//	system("pause");
	return 0;
}

int flmgen(const char* filename)
{
	int i = 0;
	char tag[64] = {0};
	char drive[256] = {0};
	char dir[256] = {0};
	const char*tips_ram = "", *tips_flashcode = "";
	uint32_t Addr[LOAD_FUN_NUM] = {0};
	uint32_t algoBlobSize = 0;
	uint32_t stackPointerAddr = MCU_RAM_BASE_ADDR + MCU_RAM_SIZE;
	uint32_t programBufferSize = 0, programBufferAddr = 0, staticDataAddr = 0;
	FILE* fout = NULL;
	char name[256] = {0};

	_splitpath(filename, drive, dir, tag, NULL);

	snprintf(name, sizeof(name), "%s%s%s%s%s.c", drive, drive[0] == 0 ? "" : "/", dir, dir[0] == 0 ? "" : "/", tag);
	fout = fopen(name, "wb+");
	if(!fout){
		debug_printf("错误：保存文件失败，请检查FLM文件的文件名是否正确！\r\n");
		goto __exit;
	};
	setbuf(fout, NULL);

	{
		char* s = tag;
		while(*s != 0)
		{
			if(*s == '_' || *s == ' ' || *s == '-' || *s == '.')
			{
				if(*s != '_')
				{
					*s = 0;
					break;
				}
				if(*(s + 1) >= '0' && *(s + 1) <= '9')  // 是数字，则直接退出
				{
					*s = 0;
					break;
				}
			}
			*s = toupper(*s);
			s++;
		}
	}

	/* 这8个数是中断halt程序，让函数执行完后返回到这里来执行从而让CPU自动halt住 */
	FlashAlgoBlob[0] = 0xE00ABE00;
	FlashAlgoBlob[1] = 0x062D780D;
	FlashAlgoBlob[2] = 0x24084068;
	FlashAlgoBlob[3] = 0xD3000040;
	FlashAlgoBlob[4] = 0x1E644058;
	FlashAlgoBlob[5] = 0x1C49D1FA;
	FlashAlgoBlob[6] = 0x2A001E52;
	FlashAlgoBlob[7] = 0x4770D1F2;

	if(FLM_Prase(filename, &FlashAlgoBlob[8], &algoBlobSize, &Addr[0],&Addr[1],&Addr[2],&Addr[3],&Addr[4]) < 0)
	{
		debug_printf("错误：解析FLM格式文件失败，请检查FLM文件是否存在或格式正确性！\r\n");
		goto __exit;
	}

	algoBlobSize += 32;

	if(MCU_RAM_SIZE == 2048)
	{
		stackPointerAddr  = MCU_RAM_BASE_ADDR + MCU_RAM_SIZE;
		programBufferAddr = MCU_RAM_BASE_ADDR + 0x300;  // flash_code <= 768 bytes
		programBufferSize = 0x200;  // 768 bytes
		staticDataAddr    = stackPointerAddr - 0x300;
		tips_ram = "!!!tips!!!: ram_less_than_2K";
	}else if(MCU_RAM_SIZE == 4096)
	{
		stackPointerAddr  = MCU_RAM_BASE_ADDR + MCU_RAM_SIZE;
		programBufferAddr = MCU_RAM_BASE_ADDR + 0x400;  // flash_code <= 1024 bytes
		programBufferSize = 0x400;  // 1024 bytes
		staticDataAddr    = stackPointerAddr - 1024;
	}else if(MCU_RAM_SIZE == 8192)
	{
		stackPointerAddr  = MCU_RAM_BASE_ADDR + MCU_RAM_SIZE;
		programBufferAddr = MCU_RAM_BASE_ADDR + 0x1000;  // flash_code <= 4096 bytes
		programBufferSize = 0x400;  // 1024 bytes
		staticDataAddr    = stackPointerAddr - 1024;
	}

	if(algoBlobSize >= 1024)
	{
		tips_flashcode = "!!!tps!!!: code_more_than_1K";
	}

	myfprintf(fout, "\r\n");
	myfprintf(fout, "#include \"flash_blob.h\"\r\n\r\n");

	myfprintf(fout, "extern const program_target_t flash_algo_%s;\r\n\r\n", tag);

	myfprintf(fout, "static const uint32_t flash_code[%d] = \n{", algoBlobSize >> 2);
	for(i = 0; i < (algoBlobSize >> 2); i++)
	{
		if(i % 8 == 0)
		{
			myfprintf(fout, "\n    ");
		}
		myfprintf(fout, "0X%08X,", FlashAlgoBlob[i]);
	}
	myfprintf(fout, "\n};\n");

	myfprintf(fout, "\r\nconst program_target_t flash_algo_%s =\n{\n", tag);
	myfprintf(fout, "    0X%08X,  // Init\n",        MCU_RAM_BASE_ADDR + 32 + Addr[0]);
	myfprintf(fout, "    0X%08X,  // UnInit\n",      MCU_RAM_BASE_ADDR + 32 + Addr[1]);
	myfprintf(fout, "    0X%08X,  // EraseChip\n",   MCU_RAM_BASE_ADDR + 32 + Addr[2]);
	myfprintf(fout, "    0X%08X,  // EraseSector\n", MCU_RAM_BASE_ADDR + 32 + Addr[3]);
	myfprintf(fout, "    0X%08X,  // ProgramPage\n", MCU_RAM_BASE_ADDR + 32 + Addr[4]);
	myfprintf(fout, "\n");
	myfprintf(fout, "    {\n");
	myfprintf(fout, "        0x%.8X,  // BKPT : start of blob + 1\n", MCU_RAM_BASE_ADDR + 1);
	myfprintf(fout, "        0x%.8X,  // RSB  : address to access global/static data\n", staticDataAddr);
	myfprintf(fout, "        0x%.8X,  // RSP  : stack pointer %s\n", stackPointerAddr, tips_ram);
	myfprintf(fout, "    },\n");
	myfprintf(fout, "\n");
	myfprintf(fout, "    0x%.8X,                      // mem buffer location\n", programBufferAddr);
	myfprintf(fout, "    0x%.8X,                      // location to write prog_blob in target RAM\n", MCU_RAM_BASE_ADDR);
	myfprintf(fout, "    sizeof(flash_code),              // prog_blob size\n");
	myfprintf(fout, "    flash_code,                      // address of prog_blob %s\n", tips_flashcode);
	myfprintf(fout, "    0x%.8X,                      // ram_to_flash_bytes_to_be_written\n", programBufferSize);
	myfprintf(fout, "};\n");
	myfprintf(fout, "\n");
	myfprintf(fout, "\n");

__exit:
	fclose(fout);
	debug_printf("!!!OK!!! ---==> [%s] \r\n", name);
	return 0;
}

static int ReadDataFromFile(const char* FName, uint32_t offset, void* buf, uint32_t size)
{
	int ret = 0;
	int fd = 0;

	if ((fd = open(FName, O_RDONLY | O_BINARY)) < 0)
	{
		ret = -1;
		goto __exit;
	}

	if (lseek(fd, offset, SEEK_SET) < 0)
	{
		ret = -2;
		goto __exit;
	}

	if (read(fd, buf, size) != size)
	{
		ret = -3;
		goto __exit;
	}

__exit:
	close(fd);
	return ret;
}

uint8_t buffer[1024 * 1024] = {0};

int FLM_Prase(const void* FName, void* pBuffer, uint32_t* Size, uint32_t* Init, uint32_t* UnInit, uint32_t* EraseChip, uint32_t* EraseSector, uint32_t* ProgramPage)
{
	int i = 0, k = 0;
	int found = 0;
	const Elf32_Phdr* pPhdr = (const Elf32_Phdr *) buffer;
	const Elf32_Shdr* pShdr = (const Elf32_Shdr *) buffer;
	const Elf32_Sym* pSymbol = (const Elf32_Sym *) buffer;
	Elf32_Ehdr ehdr = {0};      // ELF文件信息头
	Elf32_Shdr ShdrSym = {0};   // 符号表头
	Elf32_Shdr ShdrStr = {0};   // 字符串表头
	int StrFunIndexTable[LOAD_FUN_NUM] = {0};

	for(i = 0; i < LOAD_FUN_NUM; i++)
	{
		StrFunIndexTable[i] = -1;
	}

	//
	// 读取ELF文件头信息（ELF Header）
	//
	ReadDataFromFile(FName, 0, &ehdr, sizeof(Elf32_Ehdr));

	// 不是ELF格式文件
	if (strstr((const char *)ehdr.e_ident, "ELF") == NULL)
	{
		return -1;
	}

	//
	// 读取程序头信息（Program Header）
	//
	ReadDataFromFile(FName, ehdr.e_phoff, buffer, sizeof(Elf32_Phdr) * ehdr.e_phnum);
	for (i = 0; i < ehdr.e_phnum; i++)
	{
		if (pPhdr[i].p_type == PT_LOAD && (pPhdr[i].p_flags & (PF_X | PF_W | PF_R)) == (PF_X | PF_W | PF_R))
		{
			if (pPhdr[i].p_filesz > sizeof(buffer))  // RAM代码过大
			{
				return -2;
			}
			if(ReadDataFromFile(FName, pPhdr[i].p_offset, pBuffer, pPhdr[i].p_filesz) < 0)  // 提取需要下载到RAM的程序代码
			{
				return -3;
			}
//			debug_printf("====:%d\r\n", pPhdr[i].p_filesz);
			*Size = pPhdr[i].p_filesz;
		}
	}

	//
	// 读取节区头部（Sections Header）
	//
	ReadDataFromFile(FName, ehdr.e_shoff, buffer, sizeof(Elf32_Shdr) * ehdr.e_shnum);

	// 查找符号表头并拷贝出来备用
	for (i = 0; i < ehdr.e_shnum; i++)
	{
		if (pShdr[i].sh_type == SHT_SYMTAB)
		{
			memcpy(&ShdrSym, &pShdr[i], sizeof(Elf32_Shdr));

			// 查找字符串表头并拷贝出来备用
			if (pShdr[ShdrSym.sh_link].sh_type == SHT_STRTAB)
			{
				memcpy(&ShdrStr, &pShdr[ShdrSym.sh_link], sizeof(Elf32_Shdr));
				found = 1;
				break;
			}
		}
	}

	if(!found)
	{
		return -4;
	}

	//
	// 根据字符串表头读取所有字符串表
	//
	ReadDataFromFile(FName, ShdrStr.sh_offset, buffer, ShdrStr.sh_size);
	for (i = 0; i < ShdrStr.sh_size; i++)
	{
		if (buffer[i] == '\0')
		{
			buffer[i] = '\n';
		}
	}

	buffer[ShdrStr.sh_size] = 0;
#if 0
	debug_printf("------------------<< Symbols >> -----------------\n");
	debug_printf("%s\n", buffer);
	debug_printf("-------------------------------------------------\n");
#endif
	for (i = 0; i < LOAD_FUN_NUM; i++)
	{
		char* p = NULL;

		if(StrFunNameTable[i] == NULL)
			continue;

		if((p = strstr((const char *) buffer, StrFunNameTable[i])) == NULL)
			continue;

		StrFunIndexTable[i] = (uint32_t) p - (uint32_t) buffer;

		StrFunIndexTable[i] += 1; // 增加一个换行符的偏移，因为这是我们自己加的
	}

	//
	// 读取符号表
	//
	ReadDataFromFile(FName, ShdrSym.sh_offset, buffer, ShdrSym.sh_size);

	// 遍历查询我们用到的函数符号
	for (i = 0; i < ShdrSym.sh_size / sizeof(Elf32_Sym); i++, pSymbol++)
	{
		for (k = 0; k < LOAD_FUN_NUM; k++)
		{
			if (StrFunIndexTable[k] >= 0 && StrFunIndexTable[k] == pSymbol->st_name)  // symbol.st_name的值就是偏移地址
			{
				switch (k)
				{
				case 0:
					*Init = pSymbol->st_value;
					break;
				case 1:
					*UnInit = pSymbol->st_value;
					break;
				case 2:
					*EraseChip = pSymbol->st_value;
					break;
				case 3:
					*EraseSector = pSymbol->st_value;
					break;
				case 4:
					*ProgramPage = pSymbol->st_value;
					break;
				default:
					break;
				}
			}
		}
	}

	return 0;
}

